﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace KinematicCharacterController.Examples
{
    public class StressTestManager : MonoBehaviour
    {
        public Camera Camera;
        public LayerMask UIMask;

        public InputField CountField;
        public InputField SceneCountField;
        public Image RenderOn;
        public Image SimOn;
        public Image InterpOn;
        public Image RigidBodyOn;
        public ExampleCharacterController CharacterPrefab;
        public ExampleAIController AIController;
        public int SpawnCount = 100;
        public float SpawnDistance = 2f;

        public int SpawnSceneCount = 1;        
        public bool IsInterpolate = true;
        public bool IsPhysics = true;
        public bool IsRigidBody = false;
        public GameObject Floor;

        private Scene[] _AllScenes;

        private void Start()
        {
            //KinematicCharacterSystem.EnsureCreation();  
            CountField.text = SpawnCount.ToString();
            SceneCountField.text = SpawnSceneCount.ToString();
            UpdateOnImages();

            //KinematicCharacterSystem.Settings.AutoSimulation = false;
            //KinematicCharacterSystem.Settings.Interpolate = false;
        }

        private void Update()
        {
            //KinematicCharacterSystem.Simulate(Time.deltaTime, KinematicCharacterSystem.CharacterMotors, KinematicCharacterSystem.PhysicsMovers);
            DennyGuoExtend.Simulate(Time.deltaTime);
        }

        private void UpdateOnImages()
        {
            RenderOn.enabled = Camera.cullingMask == -1;
            SimOn.enabled = IsPhysics; // Physics.autoSimulation;
            InterpOn.enabled = IsInterpolate;// KinematicCharacterSystem.Settings.Interpolate;
            RigidBodyOn.enabled = IsRigidBody;
        }

        public void SetSpawnCount(string count)
        {
            if (int.TryParse(count, out int result))
            {
                SpawnCount = result;
            }
        }

        public void SetSceneCount(string count)
        {
            if (int.TryParse(count, out int result))
            {
                SpawnSceneCount = result;
            }
        }

        public void ToggleRendering()
        {
            if(Camera.cullingMask == -1)
            {
                Camera.cullingMask = UIMask;
            }
            else
            {
                Camera.cullingMask = -1;
            }
            UpdateOnImages();
        }

        public void TogglePhysicsSim()
        {
            //Physics.autoSimulation = !Physics.autoSimulation;
            IsPhysics = !IsPhysics;
            DennyGuoExtend.SetPhysicsEnable(IsPhysics);
            UpdateOnImages();
        }

        public void ToggleInterpolation()
        {
            //KinematicCharacterSystem.Settings.Interpolate = !KinematicCharacterSystem.Settings.Interpolate;
            IsInterpolate = !IsInterpolate;
            DennyGuoExtend.SetInterpolateEnable(IsInterpolate);
            UpdateOnImages();
        }

        public void ToggleRigidBody()
        {            
            IsRigidBody = !IsRigidBody;
            SetMotorRigidBodyEnable(IsRigidBody);
            UpdateOnImages();
        }

        public void Spawn()
        {
            for (int i = 0; i < AIController.Characters.Count; i++)
            {
                Destroy(AIController.Characters[i].gameObject);
            }
            AIController.Characters.Clear();

            CreateScene();
            for (int j = 0; j < SpawnSceneCount; j++)
            {
                bool needResetScene = false;
                Scene srcScene = default;
                if (SceneManager.GetActiveScene() != _AllScenes[j])
                {
                    needResetScene = true;
                    srcScene = SceneManager.GetActiveScene();
                    SceneManager.SetActiveScene(_AllScenes[j]);
                } 
     
                int charsPerRow = Mathf.CeilToInt(Mathf.Sqrt(SpawnCount));
                Vector3 firstPos = ((charsPerRow * SpawnDistance) * 0.5f) * -Vector3.one;
                firstPos.y = 0f;

                for (int i = 0; i < SpawnCount; i++)
                {
                    int row = i / charsPerRow;
                    int col = i % charsPerRow;
                    Vector3 pos = firstPos + (Vector3.right * row * SpawnDistance) + (Vector3.forward * col * SpawnDistance);

                    ExampleCharacterController newChar = Instantiate(CharacterPrefab);
                    newChar.gameObject.name += "_" + i; 
                    newChar.Motor.SetPosition(pos);

                    AIController.Characters.Add(newChar);                    
                }

                if (needResetScene)
                {
                    SceneManager.SetActiveScene(srcScene);
                }
            }

            DennyGuoExtend.SetPhysicsEnable(IsPhysics);
            DennyGuoExtend.SetInterpolateEnable(IsInterpolate);
        }

        public void CreateScene()
        {
            if (SpawnSceneCount <= 0)
            {
                return;
            }

            CreateSceneParameters parameters = new CreateSceneParameters()
            {
                localPhysicsMode = LocalPhysicsMode.Physics3D
            };

            _AllScenes = new Scene[SpawnSceneCount];
            for (int i = 1; i <= SpawnSceneCount; i++)
            {
                _AllScenes[i - 1] = SceneManager.CreateScene("Scene_" + i, parameters);
                GameObject myFloor = Instantiate(Floor);
                SceneManager.MoveGameObjectToScene(myFloor, _AllScenes[i - 1]);

                //init system
                DennyGuoExtend.EnsureCreation(_AllScenes[i - 1]);
                KinematicCharacterSystem kinematicCharacterSystem = DennyGuoExtend.GetSystem(_AllScenes[i - 1]);
                if(kinematicCharacterSystem != null)
                {
                    kinematicCharacterSystem.Settings.AutoSimulation = false;
                    kinematicCharacterSystem.Settings.Interpolate = IsInterpolate;
                }
            }
        }

        public void SetMotorRigidBodyEnable(bool value)
        {
            foreach (var characterMotor in AIController.Characters)
            {
                var motor = characterMotor.GetComponent<KinematicCharacterMotor>();
                if (motor != null && motor.enabled == value)
                {
                   motor.enabled = !value;
                }

                var rigidBody = characterMotor.GetComponent<Rigidbody>();
                if (rigidBody != null)
                {
                    rigidBody.isKinematic = !value;
                }
            }

            AIController.SetMotorRigidBodyEnable(value);
        }
    }
}